package org.evil.dog.gateway.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.ConnectTimeoutException;
import lombok.extern.log4j.Log4j2;
import org.evil.dog.gateway.enums.ErrorEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.cloud.gateway.support.TimeoutException;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.AbstractServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: 阿巳的狗
 * @Des: 网关错误处理类
 * @Date: 2021-04-30
 */
@Log4j2
public class DogGatewayErrorHandler implements ErrorWebExceptionHandler {

    @Autowired
    private LoadBalancerClient loadBalancer;

    private final String IP = "app-client-ip";

    private final String X_FORWARDED_FOR = "X-Forwarded-For";

    /**
     * 系统换行符，用于日志打印
     */
    private final String LINE_SEPARATOR = System.getProperty("line.separator");

    private final ObjectMapper mapper = new ObjectMapper();

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = response.getHeaders();
        headers.add("Content-Type", "application/json; charset=UTF-8");
        Map<String, String> res = new HashMap<>();
        if (ex instanceof TimeoutException
                || ex instanceof ConnectTimeoutException){
            this.writeLog(exchange, "业务超时");
            ((AbstractServerHttpResponse) response).setStatusCodeValue(ErrorEnum.NET_ERR.getCode());
            res.put("msg", "业务超时");
        }else if(ex instanceof ResponseStatusException) {
            HttpStatus status=  ((org.springframework.web.server.ResponseStatusException) ex).getStatus();
            this.writeLog(exchange, status.toString());
            ((AbstractServerHttpResponse) response).setStatusCodeValue(ErrorEnum.NET_ERR.getCode());
            res.put("msg", status.toString());
        }else {
            log.debug("网关错误:", ex);
            ((AbstractServerHttpResponse) response).setStatusCodeValue(ErrorEnum.GATEWAY_ERR.getCode());
            res.put("msg", "服务器错误");
        }
        String body = "{}";
        try {
            body = mapper.writeValueAsString(res);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        DataBuffer bodyDataBuffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("utf-8")));
        return response.writeWith(Mono.just(bodyDataBuffer));
    }

    /**
     * 處理義務超時信息
     * @param exchange ServerWebExchange
     * @param msg 业务超时信息
     */
    private void writeLog(ServerWebExchange exchange, String msg) {
        ServerHttpRequest request = exchange.getRequest();
        //获取真实地址
        String realUrl = this.getRealURL(exchange);
        //客户端IP
        String appIp = request.getHeaders().getFirst(IP);
        String ipChains = request.getHeaders().getFirst(X_FORWARDED_FOR);
        //api接口地址
        String apiPath = request.getURI().getPath();
        //获取真实地址
        String method = exchange.getRequest().getMethodValue();
        StringBuilder logBuffer=new StringBuilder(LINE_SEPARATOR);
        logBuffer.append("XXXXXXXXXXXXXXX错误请求XXXXXXXXXXXXXXXXXXX").append(LINE_SEPARATOR);
        logBuffer.append("接口名称:" + apiPath).append(LINE_SEPARATOR);
        logBuffer.append("终端IP:" + appIp).append(LINE_SEPARATOR);
        logBuffer.append("节点中转:" + ipChains).append(LINE_SEPARATOR);
        logBuffer.append("请求地址:" + realUrl).append(LINE_SEPARATOR);
        logBuffer.append("请求方式:" + method).append(LINE_SEPARATOR);
        logBuffer.append("错误描述:" + msg).append(LINE_SEPARATOR);
        logBuffer.append("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX").append(LINE_SEPARATOR);
        log.info(logBuffer.toString());
    }

    /**
     * 處理地址信息
     * @param exchange
     * @return
     */
    private String getRealURL(ServerWebExchange exchange) {
        //获取路由地址
        org.springframework.cloud.gateway.route.Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        if (route == null) {
            return null;
        }
        URI url = route.getUri();
        if (url == null || !"lb".equals(url.getScheme())) {
            return url != null ? url.toString() : "";
        } else {
            ServiceInstance instance = this.loadBalancer.choose(url.getHost());
            if (instance == null) {
                return null;
            } else {
                return instance.getUri().toString();
            }
        }
    }
}
